﻿using System;
using System.Drawing;
using GeoSketch2.Core;
using System.Xml;
using System.IO;

namespace GeoSketch2.Archaeo
{
    /// <summary>
    /// A benchmark is a point containing an altitude
    /// This altitude could be a reference (altitude is fixed)
    /// Or could computed on site, by levelling
    /// </summary>
    public class Benchmark:BaseArchaeoDrawableObject
    {
        #region XMLElementsName
#pragma warning disable 1591
        public const string XML_Name = "Name";
        public const string XML_Altitude = "Altitude";
        public const string XML_Position = "Position";
#pragma warning restore 1591
        #endregion

        #region Fields

        /// <summary>
        /// The position of the benchmark
        /// </summary>
        public Point2D64D Position;

        /// <summary>
        /// The altitude of the benchmark
        /// </summary>
        public double Altitude;

        /// <summary>
        /// The name of the benchmark
        /// </summary>
        public string Name;
        #endregion

        #region Constructor
        /// <summary>
        /// A point with 2D coordinates and its altitude
        /// </summary>
        /// <param name="Name">The name of the benchmark</param>
        /// <param name="Altitude">The altitude of the point</param>
        public Benchmark(string Name, double Altitude)
            : this(null,Altitude,null,null,Color.Empty,"",DateTime.Now)
        {
            this.Name = Name;
        }
        
        /// <summary>
        /// A point with 2D coordinates and its altitude
        /// </summary>
        /// <param name="Position">The location of the point</param>
        /// <param name="Altitude">The altitude of the point</param>
        /// <param name="Layer">The layer of the point</param>
        /// <param name="ExcavationUnit">The excavation unit of the object</param>
        public Benchmark(Point2D64D Position, double Altitude, Layer Layer, ExcavationArea ExcavationArea)
            : this(Position, Altitude, Layer, ExcavationArea, ColorByLayer())
        {
        }

        /// <summary>
        /// A point with 2D coordinates and its altitude
        /// </summary>
        /// <param name="Position">The location of the point</param>
        /// <param name="Altitude">The altitude of the point</param>
        /// <param name="Layer">The layer of the point</param>
        /// <param name="Color">The color of the point</param>
        /// <param name="ExcavationUnit">The excavation unit of the object</param>
        public Benchmark(Point2D64D Position, double Altitude, Layer Layer, ExcavationArea ExcavationArea, Color Color)
            : this(Position, Altitude, Layer, ExcavationArea, Color, "")
        {
        }

        /// <summary>
        /// A point with 2D coordinates and its altitude
        /// </summary>
        /// <param name="Position">The location of the point</param>
        /// <param name="Altitude">The altitude of the point</param>
        /// <param name="Layer">The layer of the point</param>
        /// <param name="Color">The color of the point</param>
        /// <param name="ExcavationUnit">The excavation unit of the object</param>
        /// <param name="Comment">The comment about of the point</param>
        public Benchmark(Point2D64D Position, double Altitude, Layer Layer, ExcavationArea ExcavationArea, Color Color, string Comment)
            : this(Position, Altitude, Layer, ExcavationArea, Color, Comment, DateTime.Now)
        {
        }

        /// <summary>
        /// A point with 2D coordinates and its altitude
        /// </summary>
        /// <param name="Position">The location of the point</param>
        /// <param name="Altitude">The altitude of the point</param>
        /// <param name="Layer">The layer of the point</param>
        /// <param name="Color">The color of the point</param>
        /// <param name="ExcavationUnit">The excavation unit of the object</param>
        /// <param name="Comment">The comment about of the point</param>
        /// <param name="CreationTime">The date of creation of the object</param>
        public Benchmark(Point2D64D Position, double Altitude, Layer Layer, ExcavationArea ExcavationArea, Color Color, string Comment, DateTime CreationTime)
            : base(Layer, Color, LineTypeByLayer(), ExcavationArea, Comment, CreationTime)
        {
            this.Position = Position;
            this.Altitude = Altitude;
            this.Name = "";
        }

        /// <summary>
        /// A point with 2D coordinates and its altitude
        /// </summary>
        /// <param name="Xr">The XmlReader to read from</param>
        /// <param name="Site">The site of the benchmark</param>
        public Benchmark(XmlReader Xr, ArchaeoProject Project)
            : base(Xr, Project)
        {
            Xr.ReadToFollowing(XML_SpecificInfos);

            #region Name
            Xr.ReadToFollowing(XML_Name);
            this.Name = Xr.ReadString();
            #endregion

            #region Position
            Xr.ReadToFollowing(XML_Position);
            Xr.ReadToFollowing(typeof(Point2D64D).FullName);
            this.Position = new Point2D64D(Xr);
            #endregion

            #region Altitude
            Xr.ReadToFollowing(XML_Altitude);
            this.Altitude = Convert.ToDouble(Xr.ReadString(), Core.Vars.Setup.GeoSketch.NumberFormat);
            #endregion

            Xr.ReadEndElement();

            Xr.ReadEndElement();

            Project.SiteBenchmarks.Add(this);
        }
        #endregion

        #region Methods
        /// <summary>
        /// Draws the benchmark if unselected
        /// </summary>
        /// <param name="Viewport">The viewport to draw to</param>
        protected override void DrawUnselected(BaseViewport Viewport)
        {
            DrawCustomPen(Viewport, Pen);
        }

        /// <summary>
        /// Draws the benchmark if unselected
        /// </summary>
        /// <param name="Viewport">The viewport to draw to</param>
        protected override void DrawSelected(BaseViewport Viewport)
        {
            DrawCustomPen(Viewport, Core.Vars.Setup.Tools.TSelection.SelectionPen);
        }

        /// <summary>
        /// Draws the benchmark with a custom pen
        /// </summary>
        /// <param name="Viewport">The viewport to draw to</param>
        /// <param name="CustomPen">The pen to draw with</param>
        public override void DrawCustomPen(BaseViewport Viewport, Pen CustomPen)
        {
            if (Position != null)
            {
                Point2D64D Point_screen = Viewport.Object2ImageTransformation.Transform(Position);
                Viewport.Graphic.DrawLine(CustomPen, Point_screen, Point_screen + new Point2D64D(-Vars.Setup.DrawableObjects.Point2D.CrossRadius, -Vars.Setup.DrawableObjects.Point2D.CrossRadius));
                Viewport.Graphic.DrawLine(CustomPen, Point_screen, Point_screen + new Point2D64D(Vars.Setup.DrawableObjects.Point2D.CrossRadius, -Vars.Setup.DrawableObjects.Point2D.CrossRadius));
                Viewport.Graphic.DrawLine(CustomPen, Point_screen, Point_screen + new Point2D64D(-Vars.Setup.DrawableObjects.Point2D.CrossRadius, Vars.Setup.DrawableObjects.Point2D.CrossRadius));
                Viewport.Graphic.DrawLine(CustomPen, Point_screen, Point_screen + new Point2D64D(Vars.Setup.DrawableObjects.Point2D.CrossRadius, Vars.Setup.DrawableObjects.Point2D.CrossRadius));

                Viewport.Graphic.DrawString(Name,
                    Vars.Setup.DrawableObjects.Benchmark.AltitudeFont,
                    CustomPen.Brush,
                    Viewport.Object2ImageTransformation.Transform(Position) + new Point2D64D(Vars.Setup.DrawableObjects.Point2D.CrossRadius + 1, Vars.Setup.DrawableObjects.Point2D.CrossRadius + 1));
            }
        }

        /// <summary>
        /// The method to test if the benchmark is near to the point clicked, for an object selection purpose
        /// </summary>
        /// <param name="Clic">The point clicked in object coordinates</param>
        /// <param name="IsNearDistance">The distance under which the clic is near to the object</param>
        public override bool IsNear(Point2D64D Clic, double IsNearDistance)
        {
            double distance = Position.Distance2D(Clic);

            if (distance < IsNearDistance)
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        /// <summary>
        /// Returns the nearest point to the click, null if none
        /// </summary>
        /// <param name="Clic">The point clicked in object coordinates</param>
        /// <param name="IsNearDistance">The distance under which the clic is near to the object</param>
        public override Point2D64D NearestPoint(Point2D64D Clic, double IsNearDistance)
        {
            if (IsNear(Clic, IsNearDistance))
            {
                return Position;
            }
            else
            {
                return null;
            }
        }

        /// <summary>
        /// Clones the object
        /// </summary>
        public override T Clone<T>()
        {
            return (T)(object)new Benchmark(Position.DeepClone(), Altitude, Layer, ExcavationArea, Color, Comment);
        }

        /// <summary>
        /// Translates the object
        /// </summary>
        /// <param name="Vector">The translation vector</param>
        public override void Translate(Point2D64D Vector)
        {
            Position.X += Vector.X;
            Position.Y += Vector.Y;
        }

        /// <summary>
        /// Writes the object to a file
        /// </summary>
        /// <param name="Xw">The XmlWriter to write to</param>
        public override void ToFile(XmlWriter Xw)
        {
            Xw.WriteStartElement(this.GetType().FullName);

            #region Object Base Information
            this.BaseInfosToFile(Xw);
            #endregion

            #region Object Specific Information
            Xw.WriteStartElement(XML_SpecificInfos);

            #region Name
            Xw.WriteStartElement(XML_Name);
            Xw.WriteString(this.Name);
            Xw.WriteEndElement();
            #endregion

            #region Position
            Xw.WriteStartElement(XML_Position);
            this.Position.ToFile(Xw);
            Xw.WriteEndElement();
            #endregion

            #region Altitude
            Xw.WriteStartElement(XML_Altitude);
            Xw.WriteString(this.Altitude.ToString(Core.Vars.Setup.GeoSketch.NumberFormat));
            Xw.WriteEndElement();
            #endregion

            Xw.WriteEndElement();
            #endregion

            Xw.WriteEndElement();
        }

        public override void ToDXFr13(TextWriter Tw)
        {
            new Point2D(this.Position, this.Layer, this.ExcavationArea, this._ObjectColor, this._LineType).ToDXFr13(Tw);

            Tw.WriteLine("0");
            Tw.WriteLine("TEXT");

            this.BaseInfosToDXFR13(Tw);

            FileIO.WriteDXFR13TextLeftBottomAligned(Tw, this.Position, this.Name, Vars.Setup.FileIO.DXFR13.DefaultTextHeight);
        }
        #endregion

        #region Properties
        /// <summary>
        /// Gets the bounding box of the object, the Y-axis direction of the REctangleD must be upside
        /// </summary>
        public override RectangleD Bounds
        {
            get
            {
                return RectangleD.FromCenter(Position, 1, 1, YAxisDirection.UpSide);
            }
        }

        /// <summary>
        /// True if the benchmark is a reference benchmark
        /// </summary>
        public bool Reference
        {
            get
            {
                return Position == null;
            }
        } 
        #endregion
    }
}
